use axum::{ extract::{Query, Path}, Json, http::HeaderMap };
use com::{ auth::claims::{get_claims, get_id}, code, query, sea_orm::{connect, crud}, utils };
use sea_orm::{entity::*, query::* };
use serde_json::json;
use model::{menu::{ ActiveModel, Column, Entity }, user};

pub async fn list(Query(param): Query<JsonValue>) -> Result<Json<JsonValue>, Json<JsonValue>>{
    let conn = connect().await?;
    let title = query::str_op(&param,"title")?;
    let name = query::str_op(&param,"name")?;
    let parent = query::i64_op(&param,"parent")?;
    let result = Entity::find()
    .select_only()
    .columns([
        Column::Id, Column::Title, Column::Name, Column::Parent, Column::Component, 
        Column::Level, Column::Disabled, Column::Icon
    ])
    // .expr_as_(date_format(Column::CreateTime, 1), "create_time")
    // .expr_as_(date_format(Column::UpdateTime, 1), "update_time")
    .apply_if(title.to_owned(), | q, v|q.filter(Column::Title.contains(v)) )
    .apply_if(name.to_owned(), | q, v|q.filter(Column::Name.like(format!("%{}%", v))) )
    .apply_if(parent, | q, v|q.filter(Column::Parent.eq(v)))
    .apply_if(query::i64_op(&param, "disabled")?, | q, v|q.filter(Column::Disabled.eq(v)))

    .order_by_asc(Column::Level)
    .into_json().all(&conn).await;
    match result {
        Ok(mut f) =>{
            let data = if title.is_none() && name.is_none() && parent.is_none() {
                utils::menu::get_menu(&mut f.as_mut())
            }else{
                f
            };
            code::data(json!(data))
        },
        Err(e) => code::sys(e.to_string())
    }
}
pub async fn save(header: HeaderMap, Json(body):Json<JsonValue>) -> Result<Json<JsonValue>, Json<JsonValue>>{
    let user = get_id(header)?;
    let conn = connect().await?;
    let mut model = ActiveModel{
        id          : NotSet,
        title       : Set( query::str(&body, "title")? ), 
        parent      : Set( query::i8_op(&body, "parent")? ),
        name        : Set( query::str(&body, "name")? ),
        level       : Set( query::i8(&body,"level")? ),
        component   : Set( query::str_op(&body, "component")? ),
        icon        : Set( query::str_op(&body, "icon")? ),
        disabled    : Set( query::u8(&body, "disabled")? ),
        create_user : Set( user.to_owned() ),
        ..Default::default()
    };
    if let Some(f) = query::i32_op(&body, "id")? {
		model.id = Set( f );
		model.update_user = Set( Some(user) );
		model.create_user = NotSet;
	}
    let result = model.save(&conn).await;
    code::send_ok(result)
}
pub async fn find(Path(id ): Path<i32>) -> Result<Json<JsonValue>, Json<JsonValue>>{
    let conn = connect().await?;
    let result = Entity::find_by_id(id)
    .select_only()
    .columns([ 
        Column::Id, Column::Title, Column::Name, Column::Parent, Column::Component, 
        Column::Level, Column::Disabled, Column::Icon
    ])
    .into_model().one(&conn).await;
    code::send_op_obj( result )
}
pub async fn del(Path(id ): Path<i32>) -> Result<Json<JsonValue>, Json<JsonValue>>{
    let conn = connect().await?;
    match Entity::find().into_json().all(&conn).await {
        Ok(f) => {
            if  f.to_owned().len() > 0 && utils::menu::exists_children(f, id) {
                code::send_ok(Entity::delete_by_id(id).exec(&conn).await)
            }else{
                code::fail(303)
            }
        },
        Err(e) => code::sys(e.to_string())
    }
}
pub async fn disabled( header: HeaderMap,Json(body):Json<JsonValue>) -> Result<Json<JsonValue>, Json<JsonValue>>{crud::bool::disabled::by_ids(header, Entity, &body).await}
// 菜单树 api
pub async fn data(header: HeaderMap) -> Result<Json<JsonValue>, Json<JsonValue>>{
    let conn = connect().await?;
    let claims = get_claims(header)?;
    let user = user::Entity::find_by_id(claims.id)
    .select_only()
    .column(user::Column::Perm).into_json().one(&conn).await;
    if claims.role == Some(0) {
        let result = Entity::find()
        .select_only()
        .columns([Column::Id, Column::Title, Column::Name, Column::Parent, Column::Icon, Column::Component ])
        .filter(Column::Disabled.eq(0))
        .order_by_asc(Column::Level)
        .into_json().all(&conn).await;
        match result {
            Ok(mut f) =>{
                let data = utils::menu::get_menu(&mut f.as_mut());
                code::data(json!(data))
            },
            Err(e) => code::sys(e.to_string())
        }
    }else{
        match user {
            Ok(f) => {
                if let Some(v) = f {
                    let perm = query::json_op(&v, "perm")?;
                    if let Some(d) = perm {
                        let parent = &mut query::vec(&d, "parent")?;
                        let mut child = query::vec(&d, "value")?;
                        let mut crud: Vec<String> = Vec::new();

                        for v in child.to_owned() {
                            let s = v.as_str().unwrap_or_default();
                            if s.contains("_") {
                                crud.push(s.to_owned());
                            }
                        }

                        if !parent.is_empty() {
                            parent.retain(|v| v.as_i64().unwrap_or_default() > 0);
                            child.append(parent);
                        }
                        let result = Entity::find()
                        .select_only()
                        .columns([Column::Id, Column::Title, Column::Name, Column::Parent, Column::Icon, Column::Component ])
                        .filter(Column::Disabled.eq(0))
                        // .filter(Column::Id.is_in(parent))
                        .filter(Column::Id.is_in(child))
                        .order_by_asc(Column::Level)
                        .into_json().all(&conn).await;
                        match result {
                            Ok(mut f) =>{
                                let mut data = utils::menu::get_menu(&mut f.as_mut());
                                let data = utils::menu::get_perm_menu(&mut data, crud);
                                code::data(json!(data))
                            },
                            Err(e) => code::sys(e.to_string())
                        }
                    }else{
                        code::data(json!([]))
                    }
                }else{
                    code::data(json!([]))
                }
            },
            Err(e) => code::sys(e.to_string())
        }
    }
}
pub async fn dict() -> Result<Json<JsonValue>, Json<JsonValue>>{
    let conn = connect().await?;
    let result = Entity::find()
    .select_only()
    .columns([ Column::Id, Column::Parent, Column::Title ])
    .filter(Column::Disabled.eq(0))
    .order_by_asc(Column::Level)
    .into_json().all(&conn).await;
    match result {
        Ok(mut f) =>{
            let data = utils::menu::get_menu(&mut f.as_mut());
            let all = json!([{ "title":"全部", "id":0, "children": data }]);
            code::data(json!(all))
        },
        Err(e) => code::sys(e.to_string())
    }
}